package com.ffcs.cn.action.labelBase.runprocess;

import com.ffcs.cn.DevelopProperties;
import com.supermap.data.Datasets;
import com.supermap.data.Datasource;
import com.supermap.data.Datasources;
import com.supermap.data.Workspace;
import com.supermap.desktop.WorkflowView.ProcessManagerPanel;
import com.supermap.desktop.WorkflowView.ui.ProcessTree;
import com.supermap.desktop.WorkflowView.ui.ProcessTreeNode;
import com.supermap.desktop.controls.ui.controls.SmDialog;
import com.supermap.desktop.controls.ui.controls.SmTextFieldSearch;
import com.supermap.desktop.controls.ui.controls.progress.RoundProgressBar;
import com.supermap.desktop.controls.utilities.JTreeUIUtilities;
import com.supermap.desktop.core.Application;
import com.supermap.desktop.core.ui.controls.GridBagConstraintsHelper;
import com.supermap.desktop.core.utilties.DatasetUtilities;
import com.supermap.desktop.core.utilties.StringUtilities;
import com.supermap.desktop.process.loader.IProcessLoader;
import com.supermap.sps.core.NameImpl;
import com.supermap.sps.core.Namespace;
import com.supermap.sps.core.NamespaceImpl;
import com.supermap.sps.core.events.ProcessRunningListener;
import com.supermap.sps.core.exeptions.ExecutionInterruptedException;
import com.supermap.sps.core.parameters.*;
import com.supermap.sps.core.workflow.IProcess;
import com.supermap.sps.core.workflow.ProcessManager;

import javax.swing.*;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;
import java.awt.*;
import java.text.MessageFormat;
import java.util.List;


public class DialogRunChooseProcess extends SmDialog {

    private JScrollPane jScrollPane;

    private SmTextFieldSearch textFieldSearch = new SmTextFieldSearch();
    /**
     * 工具箱树结构
     */
    private ProcessTree processTree;

    private JLabel jLabelKey;
    private JTextField textFieldKey;

    private RoundProgressBar progressBar;
    private JTextArea textAreaInfo;
    private JButton buttonExecute;
    /**
     * 叠加分析算子key，这里是示例使用叠加分析key，对应获取方式
     * @see DialogRunChooseProcess#getProcessKey(IProcessLoader) 中的获取写法
     */
    private static final String intersectDatasetProcessKey = "iobjects_vectoranalyst-com.supermap.analyst.vectoranalyst.overlay-intersectdataset";

    /**
     * 执行事件
     */
    private final ProcessRunningListener processRunningListener = event -> {
        String message = event.getMessage();
        Application.getActiveApplication().getOutput().output(message);
        int progress = event.getProgress();
        this.progressBar.setProgress(progress);
    };

    public DialogRunChooseProcess() {
        initComponents();
        initLayout();
        registerEvents();
        initComponentState();
    }

    private void initComponents() {
        this.initProcessTree();
        this.jScrollPane = new JScrollPane(processTree);
        this.buttonExecute = new JButton(DevelopProperties.getString("String_Execute"));
        this.textFieldKey = new JTextField();
        this.jLabelKey = new JLabel();
        this.progressBar = new RoundProgressBar();
        this.textAreaInfo = new JTextArea();
    }

    /**
     * 界面布局
     */
    private void initLayout() {
        JPanel leftPanel = new JPanel();
        leftPanel.setLayout(new GridBagLayout());
        leftPanel.add(textFieldSearch, new GridBagConstraintsHelper(0, 0).setWeight(1, 0).setFill(GridBagConstraints.HORIZONTAL).setInsets(10, 10, 0, 0));
        leftPanel.add(jScrollPane, new GridBagConstraintsHelper(0, 1).setWeight(1, 1).setFill(GridBagConstraints.BOTH).setInsets(5, 10, 10, 0));

        JPanel footPanel = new JPanel();
        footPanel.setLayout(new GridBagLayout());
        footPanel.add(progressBar, new GridBagConstraintsHelper(0, 0, 3, 1)
                .setAnchor(GridBagConstraints.WEST)
                .setInsets(0, 0, 0, 0)
                .setFill(GridBagConstraints.BOTH)
                .setWeight(1, 0));

        footPanel.add(buttonExecute, new GridBagConstraintsHelper(3, 0, 1, 1)
                .setAnchor(GridBagConstraints.WEST)
                .setInsets(0, 0, 0, 0)
                .setFill(GridBagConstraints.NONE)
                .setWeight(0, 0));

        JPanel rightPanel = new JPanel();
        rightPanel.setLayout(new GridBagLayout());
        rightPanel.add(jLabelKey, new GridBagConstraintsHelper(0, 0, 1, 1)
                .setAnchor(GridBagConstraints.NORTHWEST)
                .setInsets(GridBagConstraintsHelper.CONTROLS_GAP, 0, GridBagConstraintsHelper.CONTROLS_GAP, GridBagConstraintsHelper.FRAME_CONTROL_GAP)
                .setFill(GridBagConstraints.NONE)
                .setWeight(0, 0));

        rightPanel.add(textFieldKey, new GridBagConstraintsHelper(1, 0, 3, 1)
                .setAnchor(GridBagConstraints.NORTHWEST)
                .setInsets(GridBagConstraintsHelper.CONTROLS_GAP, 0, GridBagConstraintsHelper.CONTROLS_GAP, GridBagConstraintsHelper.FRAME_CONTROL_GAP)
                .setFill(GridBagConstraints.HORIZONTAL)
                .setWeight(1, 0));
        rightPanel.add(new JScrollPane(textAreaInfo), new GridBagConstraintsHelper(0, 1, 4, 2)
                .setAnchor(GridBagConstraints.WEST)
                .setInsets(GridBagConstraintsHelper.CONTROLS_GAP, 0, GridBagConstraintsHelper.CONTROLS_GAP, GridBagConstraintsHelper.FRAME_CONTROL_GAP)
                .setFill(GridBagConstraints.BOTH)
                .setWeight(1, 1));

        rightPanel.add(footPanel, new GridBagConstraintsHelper(0, 3, 4, 1)
                .setAnchor(GridBagConstraints.NORTHWEST)
                .setInsets(GridBagConstraintsHelper.CONTROLS_GAP, 0, GridBagConstraintsHelper.CONTROLS_GAP, GridBagConstraintsHelper.FRAME_CONTROL_GAP)
                .setFill(GridBagConstraints.HORIZONTAL)
                .setWeight(1, 0));


        this.setLayout(new GridBagLayout());
        leftPanel.setPreferredSize(new Dimension(300, 10));
        rightPanel.setPreferredSize(new Dimension(15, 15));

        this.add(leftPanel, new GridBagConstraintsHelper(0, 0, 1, 4)
                .setAnchor(GridBagConstraints.WEST)
                .setInsets(GridBagConstraintsHelper.CONTROLS_GAP, GridBagConstraintsHelper.FRAME_CONTROL_GAP, GridBagConstraintsHelper.CONTROLS_GAP, GridBagConstraintsHelper.FRAME_CONTROL_GAP)
                .setFill(GridBagConstraints.BOTH)
                .setWeight(0, 1));

        this.add(rightPanel, new GridBagConstraintsHelper(1, 0, 1, 4)
                .setAnchor(GridBagConstraints.WEST)
                .setInsets(GridBagConstraintsHelper.CONTROLS_GAP, 0, GridBagConstraintsHelper.CONTROLS_GAP, GridBagConstraintsHelper.FRAME_CONTROL_GAP)
                .setFill(GridBagConstraints.BOTH)
                .setWeight(1, 1));

    }

    private void initComponentState() {
        this.setSize(new Dimension(1050, 800));
        this.jLabelKey.setText(DevelopProperties.getString("String_RunProcessKey"));
        this.setTitle(DevelopProperties.getString("String_RunProcessTitle"));
        this.setModal(false);
    }

    private void initProcessTree() {
        final ProcessManagerPanel processManagerPanel = (ProcessManagerPanel) Application.getActiveApplication().getMainFrame().getDockbarManager().get(ProcessManagerPanel.class.getName()).getInnerComponent();
        // 从已有的树中构建出算子树
        ProcessTree processTree = processManagerPanel.getProcessTree();
        this.processTree = new ProcessTree((TreeNode) processTree.getModel().getRoot()).setShowPopupMenu(false);
    }

    private void registerEvents() {
        this.buttonExecute.addActionListener(evt -> {
            this.textAreaInfo.setText("");
            String processKey = this.textFieldKey.getText();
            //根据processKey创建算子
            processKey = intersectDatasetProcessKey;
            IProcess process = createProcess(processKey);
            //初始化算子参数值
            initProcessInput(process);
            this.progressBar.setProgress(0);
            this.progressBar.updateProgressIndeterminate();
            new Thread(() -> {
                try {
                    process.addProcessRunningListener(this.processRunningListener);
                    boolean execute = process.execute();
                    if (execute) {
                        updateResultLog(process, true, null);
                        this.progressBar.setProgress(100);
                        this.progressBar.stopUpdateProgressIndeterminate();
                    }
                    process.removeProcessRunningListener(this.processRunningListener);
                } catch (ExecutionInterruptedException ex) {
                    updateResultLog(process, false, ex);
                    Application.getActiveApplication().getOutput().output(ex);
                    process.removeProcessRunningListener(this.processRunningListener);
                    throw new RuntimeException(ex);
                }
            }).start();
        });

        this.processTree.addTreeSelectionListener(e -> {
            DefaultMutableTreeNode lastPathComponent = (DefaultMutableTreeNode) e.getPath().getLastPathComponent();
            Object userObject = lastPathComponent.getUserObject();
            if (userObject instanceof IProcessLoader) {
                // 通过树结构节点的userObject中拿到算子的key
                String processKey = getProcessKey((IProcessLoader) userObject);
                this.textFieldKey.setText(processKey);
                // 根据算子key构建出process，能拿到输入，输出定义
                IProcess process = createProcess(processKey);
                List<IInput> inputs = process.getInputs();
                StringBuilder stringBuilder = new StringBuilder("inputs:\n");
                for (IInput input : inputs) {
                    stringBuilder.append("  ").append(input.getTitle()).append("(").append(input.getName()).append(")").append(" : ").append(input.getDataDefinition().getType()).append("\n");
                }
                List<IOutput> outputs = process.getOutputs();
                stringBuilder.append("outputs:\n");
                for (IOutput output : outputs) {
                    stringBuilder.append("  ").append(output.getTitle()).append("(").append(output.getName()).append(")").append(output.getDataDefinition().getType()).append("\n");
                }
                textAreaInfo.setText(stringBuilder.toString());
            }
        });

        textFieldSearch.getDocument().addDocumentListener(new DocumentListener() {
            @Override
            public void insertUpdate(DocumentEvent e) {
                search();
            }

            @Override
            public void removeUpdate(DocumentEvent e) {
                search();
            }

            @Override
            public void changedUpdate(DocumentEvent e) {
                search();
            }
        });
    }

    private void search() {
        // 搜索目前是这么实现的，设置完搜索字符串之后，重新加载树
        ProcessTreeNode.search(textFieldSearch.getText());
        reloadTree(processTree);
    }

    private void reloadTree(JTree tree) {
        TreePath selectionPath = tree.getSelectionPath();
        ProcessTreeNode root = (ProcessTreeNode) tree.getModel().getRoot();
        root.refreshNodeState();
        ((DefaultTreeModel) tree.getModel()).reload();
        boolean expand = !StringUtilities.isNullOrEmpty(textFieldSearch.getText());
        JTreeUIUtilities.expandTree(tree, expand);
        if (!expand) {
            if (selectionPath != null) {
                JTreeUIUtilities.locateNode(tree, selectionPath);
            } else {
                tree.expandPath(new TreePath(tree.getModel().getRoot()));
            }
        }
    }

    private void updateResultLog(IProcess process, boolean execute, Throwable throwable) {
        List<IOutput> outputs = process.getOutputs();
        if (outputs.isEmpty()) {
            return;
        }
        if (execute) {
            textAreaInfo.append(DevelopProperties.getString("String_ProcessRunSuccess"));
            textAreaInfo.append("\n");
            textAreaInfo.append(DevelopProperties.getString("String_ProcessOutputName"));
            textAreaInfo.append("\n");
            for (IOutput output : outputs) {
                textAreaInfo.append(MessageFormat.format(DevelopProperties.getString("String_ProcessParamName"), output.getTitle()));
                textAreaInfo.append("\n");
                if (output instanceof ISingleOutput) {
                    textAreaInfo.append(MessageFormat.format(DevelopProperties.getString("String_ProcessParamValue"), ((ISingleOutput<?>) output).getValue()));
                    textAreaInfo.append("\n");
                } else if (output instanceof ICollectionOutput) {
                    textAreaInfo.append(MessageFormat.format("String_ProcessParamValue", ((ICollectionOutput<?>) output).getValueList()));
                    textAreaInfo.append("\n");
                }
            }
        } else {
            textAreaInfo.append(DevelopProperties.getString("String_ProcessRunError"));
            textAreaInfo.append("\n");
            textAreaInfo.append(throwable.getMessage());
        }
    }

    /**
     * 根据key构建出Process对象
     */
    private IProcess createProcess(String processKey) {
        IProcess process;
        String[] split = processKey.split("-");
        String factoryKey = split[0];
        Namespace namespace = new NamespaceImpl(split[1], "");
        NameImpl name = new NameImpl(namespace, split[2]);
        // 根据key创建算子
        process = ProcessManager.instance().create(factoryKey, name);
        return process;
    }

    private void initProcessInput(IProcess process) {
        try {
            List<IInput> inputs = process.getInputs();
            //叠加分析，参数设置示例，当前打开桌面必须有打开的工作空间，并且有对应的数据源、数据集信息
            Workspace workspace = Application.getActiveApplication().getWorkspace();
            Datasources datasources = workspace.getDatasources();
            Datasource datasource = datasources.get("DataSource");
            Datasets datasets = datasource.getDatasets();
            for (IInput iInput : inputs) {
                //根据输入参数，title名称设置对应的值，下面以叠加分析示例
                if (iInput.isRequired()) {
                    Object value = new Object();
                    String inputName = iInput.getName();
                    if ("dataset".equals(inputName)) {
                        value = datasets.get("NewPoint");
                    } else if ("clipDataset".equals(inputName)) {
                        value = datasets.get("NewRegion");
                    } else if ("resultDatasource".equals(inputName)) {
                        value = datasource;
                    } else if ("resultDatasetName".equals(inputName)) {
                        value = DatasetUtilities.getAvailableDatasetName(datasource, "result_dataset");
                    }
                    setValue(iInput, value);
                    if (textAreaInfo.getText() == null || textAreaInfo.getText().isEmpty()) {
                        textAreaInfo.append(DevelopProperties.getString("String_ProcessInputName"));
                        textAreaInfo.append("\n");
                    }
                    textAreaInfo.append(MessageFormat.format(DevelopProperties.getString("String_ProcessParamName"), iInput.getTitle()));
                    textAreaInfo.append("\n");
                    textAreaInfo.append(MessageFormat.format(DevelopProperties.getString("String_ProcessParamValue"), value));
                    textAreaInfo.append("\n");
                }
            }
        } catch (Exception e) {
            Application.getActiveApplication().getOutput().output(DevelopProperties.getString("String_ProcessInitError"));
            textAreaInfo.append(DevelopProperties.getString("String_ProcessInitError"));
            Application.getActiveApplication().getOutput().output(e);
            throw e;
        }
    }

    private String getProcessKey(IProcessLoader iProcessLoader) {
        String factoryKey = iProcessLoader.getFactoryKey();
        String namespace = iProcessLoader.getNamespace().getNamespace();
        String simpleName = iProcessLoader.getSimpleName();
        return factoryKey + "-" + namespace + "-" + simpleName;
    }

    private void setValue(IInput iInput, Object value) {
        // 根据不同的类型设置不同的ValueProvider
        if (iInput instanceof ISingleInput) {
            if (((ISingleInput) iInput).getValueProvider() instanceof IPreservableValueProvider || ((ISingleInput) iInput).getValueProvider() == null) {
                iInput.setValueProvider(createSingleValueProvider(iInput, value));
            }
        } else if (iInput instanceof ICollectionInput) {
            iInput.setValueProvider(createCollectionValueProvider(iInput, value));
        }
    }

    private ISingleValueProvider createSingleValueProvider(IInput input, Object value) {
        return new SampleCodeSingleValueProvider(input, value == null ? input.getDefaultValue() : value);
    }

    private ICollectionValueProvider createCollectionValueProvider(IInput input, Object value) {
        return new SampleCodeCollectionsValueProvider(input, value == null ? input.getDefaultValue() : value);
    }

}
